/*
 * Copyright (c) 2003-2022 The Apereo Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *             http://opensource.org/licenses/ecl2
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.sakaiproject.content.tool.inputpreserver;

import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.sakaiproject.cheftool.Context;
import org.sakaiproject.cheftool.RunData;
import org.sakaiproject.content.tool.CopyrightDelegate;
import org.sakaiproject.event.api.SessionState;
import org.sakaiproject.util.ParameterParser;

/**
 * Descendents of this class define a list of UI elements for user input.
 * When forms are processed, this class is used to save user input into the state.
 * When validation errors occur or the like, this class moves the data from the state back into the context to avoid user data loss.
 */
public abstract class UserInputPreserver
{
	/**
	 * Returns a namespace for any state keys generated by this class to eliminate the risk of conflicts.
	 */
	public abstract String getNamespace();

	/**
	 * Key set: the keys used to get user input from data.getParameters()
	 * Value set: the keys used to put these values back into the UI
	 */
	public abstract Map<String, String> getParamToContextKeys();

	/**
	 * Parameters can be of different java types.
	 * Implementation should switch on the key, then return the parsed value in its appropriate type.
	 */
	public abstract Object parseParameter(ParameterParser params, String key, SessionState state);

	/**
	 * Stores form data from RunData.getParameters() into the SessionState for each key in getParamToContextKeys().
	 * Call this before validating user input.
	 */
	public void saveFormData(RunData data, SessionState state)
	{
		ParameterParser params = data.getParameters();
		getParamToContextKeys().keySet().stream().forEach(key ->
		{
			Object value = parseParameter(params, key, state);
			state.setAttribute(getNamespacedKey(key), value);
		});
	}

	/**
	 * Puts all user input preserved in the SessionState by this class back into the context.
	 */
	public void reloadFormData(SessionState state, Context context)
	{
		getParamToContextKeys().entrySet().stream().forEach(entry ->
		{
			Object preservedUserInput = state.getAttribute(getNamespacedKey(entry.getKey()));
			if (preservedUserInput instanceof String)
			{
				if (!StringUtils.isBlank((String)preservedUserInput))
				{
					context.put(entry.getValue(), preservedUserInput);
				}
			}
			else if (preservedUserInput instanceof CopyrightDelegate)
			{
				CopyrightDelegate cd = (CopyrightDelegate)preservedUserInput;
				cd.reloadSelectionsInContext(context);
			}
			else if (preservedUserInput != null)
			{
				context.put(entry.getValue(), preservedUserInput);
			}
		});
	}

	/**
	 * Clears preserved user input from the SessionState for each key generated by this class.
	 * Call this when form processing succeeds, or when navigating to a different view.
	 */
	public void clearFormData(SessionState state)
	{
		getParamToContextKeys().keySet().stream().forEach(key ->
		{
			state.removeAttribute(getNamespacedKey(key));
		});
	}

	/**
	 * Namespaces the specified key for the SessionState
	 */
	public String getNamespacedKey(String key)
	{
		return new StringBuilder(getNamespace()).append("_").append(key).toString();
	}
}
